package cn.maihe.elg.operation.supports.bank;

//import api.ApiResponse;
//import api.SPDBSMEncryptor;
import cn.maihe.elg.operation.config.SpdBankBiSafeConfig;
//import cn.maihe.elg.operation.config.SpdBankConfig;
import cn.maihe.elg.operation.model.dto.BaseReq;
import cn.maihe.elg.operation.model.dto.BaseResp;
import cn.maihe.elg.operation.supports.bank.dto.*;
import cn.maihe.elg.operation.supports.bank.enums.SpdbMethod;
//import cn.maihe.elg.operation.supports.bank.enums.SpdbRespCode;
import cn.maihe.elg.operation.utils.*;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

/**
 * @Description 接口统一识别码 OPC103100201000300008900
 * 调用方法 GET
 * 编码格式 UTF-8
 * 报文体格式 URL参数
 * 测试环境URL https://etest4.spdb.com.cn/spdb/uat/api/fundDeposits/eDeposits/accountDetails
 * 生产环境URL https://api.spdb.com.cn/spdb/prd/api/fundDeposits/eDeposits/accountDetails
 * 旧版URL
 * 调用方法：POST
 * 测试：https://etest4.spdb.com.cn/spdb/uat/api/fundDeposits/eDeposits/accountDetails/post
 * 生产：https://api.spdb.com.cn/spdb/prd/api/fundDeposits/eDeposits/accountDetails/post
 * 必输标识 Y或M：必须填写的域；
 * C：某条件成立时必须填写的域；
 * N或O：可选填写的域。
 * 接入方式 全报文加密
 * @Author WJH
 * @Date 2021/11/25
 */
@Service
@Slf4j
public abstract class BaseSpdBankTransService<REQ extends BaseReq, RESP extends BaseResp> {

    protected Class<REQ> reqClass;
    protected Class<RESP> respClass;

//    @Resource
//    protected SpdBankConfig spdBankConfig;
    @Resource
    protected SpdBankBiSafeConfig spdBankBiSafeConfig;

    public BaseSpdBankTransService() {
        Type[] types = ((ParameterizedType) (getClass().getGenericSuperclass())).getActualTypeArguments();
        this.reqClass = (Class<REQ>) types[0];
        this.respClass = (Class<RESP>) types[1];
    }

    @PostConstruct
    public void init() {
//        Assert.notNull(spdBankConfig, () -> "浦发银企直连信息未配置");
//        ValidateUtil.validate(spdBankConfig);
    }

    protected abstract SpdbMethod spdbMethod();

//    protected String buildURL() {
//        return String.format("%s/%s", spdBankConfig.getSpdBaseApiUrl(), spdbMethod().getPathUri());
//    }

//    protected RESP sendSPDBApi(REQ req) {
//        String traceId = IdGenerate.genTranceId();
//        //请求参数校验
//        ValidateUtil.validate(req);
//        //序列化对象
//        String requestBody = JSON.toJSONString(req);
//
//        String clientId = spdBankConfig.getXSpdbClientId();
//        String secret = spdBankConfig.getXSpdbClientSecret();
//        String privatekey = spdBankConfig.getZtSm2PriKey();
//        String spdbPublicKey = spdBankConfig.getSpdSm2PubKey();
//        String method = spdbMethod().getMethodName();
//        String url = this.buildURL();
//        Boolean forbidden = spdbMethod().getForbidden() != null ? spdbMethod().getForbidden() : spdBankConfig.getForbidden();
//        Boolean encryption = spdbMethod().getEncryption() != null ? spdbMethod().getEncryption() : spdBankConfig.getEncryption();
//
//        try {
//            ElgLogger.info(log, l -> l.info("{} >> {} 请求地址:{}", traceId, spdbMethod(), buildURL()));
//            ElgLogger.info(log, l -> l.info("{} >> {} 请求报文:{}", traceId, spdbMethod(), requestBody));
//            ApiResponse apiResponse = SPDBSMEncryptor.requestApi(clientId, secret, privatekey, spdbPublicKey, method, url, requestBody, forbidden, encryption);
//            Assert.notNull(apiResponse, () -> "浦发响应报文为空");
//            ElgLogger.info(log, l -> l.info("{} << {} 响应报文:{}", traceId, spdbMethod(), JSON.toJSONString(apiResponse)));
//
//            SpdbRespCode spdbRespCode = SpdbRespCode.resolve(apiResponse.getCode());
//            if (spdbRespCode != SpdbRespCode.SUCCESS) {
//                throw new RuntimeException("响应码非正常[" + apiResponse.getCode()+ "]");
//            }
//            if (apiResponse.getVerify() == null || !apiResponse.getVerify()) {
//                throw new RuntimeException("响应验签失败[" + apiResponse.getVerify() + "]");
//            }
//
//            String decryptBody = apiResponse.getDecryptBody();
//            Assert.hasText(decryptBody, () -> "响应解密报文为空");
//
//            RESP resp = JSON.parseObject(decryptBody, this.respClass);
//
//            return resp;
//        } catch (Exception e) {
//            ElgLogger.error(log, l -> l.error("{} << {} 请求响应异常:{}", traceId, spdbMethod(), e.getMessage(), e));
//            throw new RuntimeException("处理异常:" + e.getMessage());
//        }
//    }

    public REQ toReq(Object obj){
        return JSON.parseObject(JSON.toJSONBytes(obj),this.reqClass);
    }

    protected AccountDetailRespDTO sendSPDBBiSafe(AccountDetailReqDTO req) {
        String traceId = IdGenerate.genTranceId();
        AccountDetailRespDTO accountDetailRespDTO = new AccountDetailRespDTO();
        try {
            String reqXmlStr = XmlUtil.objectToNSXmlStr(req, AccountDetailReqDTO.class);
            ElgLogger.info(log, l -> l.info("{} >> {} 请求报文原文:{}", traceId, spdbMethod(), reqXmlStr));
//            ElgLogger.info(log, l -> l.info("{} >> {} 请求报文原文head:{}", traceId, spdbMethod(), req.getHead()));
            //step1: 将body明文报文进行签名, 得到签名后的密文
            String body = XmlUtil.objectToNSXmlStr(req.getClearBody(), AccountDetailReqBody.class);
//            ElgLogger.info(log, l -> l.info("{} >> {} 请求报文原文body:{}", traceId, spdbMethod(), body));
            ElgLogger.info(log, l -> l.info("{} >> {} 签名请求明文:{}", traceId, spdbMethod(), body));
            InputStream in = SPDBUtil.send(body.getBytes("GBK"), spdBankBiSafeConfig.getSignContentType(), spdBankBiSafeConfig.getSignUrl());
            String sign = SPDBUtil.parseSignContent(in);
            in.close();
            Assert.notNull(sign, () -> "浦发请求报文Body签名结果为空");
            //step2: 得到密文后拼接报文，发送交易
            BaseSpdBankBodySign baseSpdBankBodySign = new BaseSpdBankBodySign();
            baseSpdBankBodySign.setSignature(sign);
            req.setBody(baseSpdBankBodySign);
            req.setClearBody(null);
            String xmlString = XmlUtil.objectToXmlStr(req, AccountDetailReqDTO.class);
            String xmlLengthStr = String.format("%06d", xmlString.length());
            String reqStr = xmlLengthStr + xmlString;
//            ElgLogger.info(log, l -> l.info("{} >> {} 请求报文密文:{}", traceId, spdbMethod(), reqStr));
            String result = SPDBUtil.sendByHttp(reqStr, spdBankBiSafeConfig.getSendType(), spdBankBiSafeConfig.getHost(), spdBankBiSafeConfig.getPort());
            Assert.notNull(result, () -> "浦发响应报文为空");
//            ElgLogger.info(log, l -> l.info("{} << {} 响应报文原文:{}", traceId, spdbMethod(), result));
            accountDetailRespDTO = (AccountDetailRespDTO) XmlUtil.xmlStrToObject(this.respClass, result.trim());
            String returnCode = accountDetailRespDTO.getHead().getReturnCode();
            String returnMsg = accountDetailRespDTO.getHead().getReturnMsg();
            if (!"AAAAAAA".equals(returnCode)) {
                ElgLogger.info(log, l -> l.info("{} << {} 响应报文原文:{}", traceId, spdbMethod(), result));
                throw new RuntimeException("浦发响应报文头 returnCode:" + returnCode + " returnMsg:" + returnMsg);
            }
            //step3: 将返回报文中的signature密文进行验签，获取交易返回的body明文
            String retSign = result.substring(result.indexOf("<signature>") + 11, result.indexOf("</signature>"));
            InputStream in2 = SPDBUtil.send(retSign.getBytes(), spdBankBiSafeConfig.getVerifyContentType(), spdBankBiSafeConfig.getVerifyUrl());
            Map verify = SPDBUtil.parseVerifyContent(in2);
            in2.close();
            Assert.notNull(verify, () -> "浦发响应报文Body验签结果为空");
            //step3: 得到body明文后写入返回对象
            String respBody = verify.get("plain").toString();
//            AccountDetailRespDTO finalAccountDetailRespDTO = accountDetailRespDTO;
//            ElgLogger.info(log, l -> l.info("{} >> {} 响应head明文:{}", traceId, spdbMethod(), finalAccountDetailRespDTO.getHead()));
            ElgLogger.info(log, l -> l.info("{} >> {} 响应body明文:{}", traceId, spdbMethod(), respBody));
            AccountDetailRespBody accountDetailRespBody = (AccountDetailRespBody) XmlUtil.xmlStrToObject(AccountDetailRespBody.class, respBody);
            accountDetailRespDTO.setClearBody(accountDetailRespBody);
        } catch (Exception e) {
            ElgLogger.error(log, l -> l.error("{} << {} 请求响应异常:{}", traceId, spdbMethod(), e.getMessage(), e));
            throw new RuntimeException("处理异常:" + e.getMessage());
        }
        return accountDetailRespDTO;
    }
}
